#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;

namespace PHMEE.EnergyEfficiency;

/// <summary>
/// 航速和 CII 的映射关系
/// </summary>
public sealed class VToCII
{
    public VToCII(decimal v, decimal cii)
    {
        V = v;
        CII = cii;
    }

    private VToCII()
    {
    }

    /// <summary>
    /// 航速
    /// </summary>
    public decimal V { get; set; }

    /// <summary>
    /// CII
    /// </summary>
    public decimal CII { get; set; }
}

public struct MinMax
{
    public decimal Min { get; set; }

    public decimal Max { get; set; }
}

public static class VToCIIExtensions
{
    public static float? FindClosestCIIByV(this List<VToCII> vToCIIs, float v)
    {
        var vDecimal = (decimal)v;
        var minMax = vToCIIs.GetVBounds();
        if (vDecimal < minMax.Min || vDecimal > minMax.Max)
        {
            // throw new Exception($"V is out of calculation range. Range: {minMax.Min} - {minMax.Max}");
            return null;
        }

        return vToCIIs.ClosestCIIByV(v);
    }
    
    private static float? ClosestCIIByV(this List<VToCII> vToCIIs, float v)
    {
        var vDecimal = (decimal)v;
        VToCII? equalV = vToCIIs.FirstOrDefault(x => x.V == vDecimal);
        if (equalV != null)
        {
            return (float)equalV.CII;
        }
        VToCII? largerV = vToCIIs.FindJustLargerV(vDecimal);
        VToCII? smallerV = vToCIIs.FindJustSmallerV(vDecimal);

        if (largerV is null && smallerV is null)
        {
            // throw new Exception("Database does not contain any V values.");
            return null;
        }

        if (largerV is null)
        {
            return (float)smallerV!.CII;
        }

        if (smallerV is null)
        {
            return (float)largerV.CII;
        }

        float diffLarger = (float)Math.Abs(largerV.V - vDecimal);
        float diffSmaller = (float)Math.Abs(vDecimal - smallerV.V);

        // If the differences are equal or smallerV difference is smaller, return the smaller V's CII value.
        return diffLarger < diffSmaller
            ? (float)largerV.CII
            : (float)smallerV.CII;
    }


    public static float? FindClosestVByCII(this List<VToCII> vToCIIs, float cii)
    {
        var ciiDecimal = (decimal)cii;
        var minMax = vToCIIs.GetCIIBounds();
        if (ciiDecimal < minMax.Min || ciiDecimal > minMax.Max)
        {
            // throw new Exception($"CII is out of calculation range. Range: {minMax.Min} - {minMax.Max}");
            return null;
        }

        return vToCIIs.ClosestVByCII(cii);
    }

    private static float? ClosestVByCII(this List<VToCII> vToCIIs, float cii)
    {
        var ciiDecimal = (decimal)cii;

        VToCII? largerCII = vToCIIs.FindJustLargerCII(ciiDecimal);
        VToCII? smallerCII = vToCIIs.FindJustSmallerCII(ciiDecimal);

        if (largerCII is null && smallerCII is null)
        {
            // throw new Exception("Database does not contain any CII values.");
            return null;
        }

        if (largerCII is null)
        {
            return (float)smallerCII!.V;
        }

        if (smallerCII is null)
        {
            return (float)largerCII.V;
        }

        float diffLarger = (float)Math.Abs(largerCII.CII - ciiDecimal);
        float diffSmaller = (float)Math.Abs(ciiDecimal - smallerCII.CII);

        // If the differences are equal or smallerCII difference is smaller, return the smaller CII's V value.
        return diffLarger < diffSmaller
            ? (float)largerCII.V
            : (float)smallerCII.V;
    }

    #region 底层方法，来自 Desktop 版的 Repository

    /// <summary>
    /// 根据航速 V 查找对应的 CII
    /// </summary>
    /// <param name="vToCIIs"></param>
    /// <param name="v"></param>
    /// <returns></returns>
    private static decimal? FindCIIByV(this List<VToCII> vToCIIs, decimal v)
    {
        return vToCIIs.FirstOrDefault(x => x.V == v)?.CII;
    }

    /// <summary>
    /// 查找大于给定 CII 的最小 CII
    /// </summary>
    /// <param name="vToCIIs"></param>
    /// <param name="cii"></param>
    /// <returns></returns>
    private static VToCII? FindJustLargerCII(this List<VToCII> vToCIIs, decimal cii)
    {
        return vToCIIs.Where(x => x.CII > cii).MinBy(x => x.CII);
    }

    /// <summary>
    /// 查找大于给定航速 V 的最小 V
    /// </summary>
    /// <param name="vToCIIs"></param>
    /// <param name="v"></param>
    /// <returns></returns>
    private static VToCII? FindJustLargerV(this List<VToCII> vToCIIs, decimal v)
    {
        return vToCIIs.Where(x => x.V > v).MinBy(x => x.V);
    }

    /// <summary>
    /// 查找小于给定 CII 的最大 CII
    /// </summary>
    /// <param name="vToCIIs"></param>
    /// <param name="cii"></param>
    /// <returns></returns>
    private static VToCII? FindJustSmallerCII(this List<VToCII> vToCIIs, decimal cii)
    {
        return vToCIIs.Where(x => x.CII < cii).MaxBy(x => x.CII);
    }

    /// <summary>
    /// 查找小于给定航速 V 的最大 V
    /// </summary>
    /// <param name="vToCIIs"></param>
    /// <param name="v"></param>
    /// <returns></returns>
    private static VToCII? FindJustSmallerV(this List<VToCII> vToCIIs, decimal v)
    {
        return vToCIIs.Where(x => x.V < v).MaxBy(x => x.V);
    }

    // /// <summary>
    // /// 根据精确的 CII 查找对应的 V
    // /// </summary>
    // /// <param name="vToCIIs"></param>
    // /// <param name="cii"></param>
    // /// <returns></returns>
    // /// <exception cref="InvalidOperationException"></exception>
    // public static VToCII FindVByExactCII(this List<VToCII> vToCIIs, decimal cii)
    // {
    //     var result = vToCIIs.FirstOrDefault(x => x.CII == cii);
    //     if (result == null)
    //     {
    //         throw new InvalidOperationException("No V found for the specified CII.");
    //     }
    //
    //     return result;
    // }

    /// <summary>
    /// 获取CII的最小和最大值
    /// </summary>
    /// <returns></returns>
    private static MinMax GetCIIBounds(this List<VToCII> vToCIIs)
    {
        return new MinMax
        {
            Min = vToCIIs.Min(x => x.CII),
            Max = vToCIIs.Max(x => x.CII)
        };
    }

    /// <summary>
    /// 获取V的最小和最大值
    /// </summary>
    /// <returns></returns>
    private static MinMax GetVBounds(this List<VToCII> vToCIIs)
    {
        return new MinMax
        {
            Min = vToCIIs.Min(x => x.V),
            Max = vToCIIs.Max(x => x.V)
        };
    }

    #endregion
}